#include <iostream>
#include <map>
#include "./md5/md5wrapper.h"  // ver http://www.md5hashing.com/algorithm/


#include <boost/tr1/memory.hpp>

#include "Repository.h"
#include "../classes_teste/Estatistica.h"
#include "../classes_outros/Config.h"

#include <ctime>
#include <cstdio>

using namespace std;

std::tr1::shared_ptr<Repository> Repository::theRepository(new Repository());
boost::mutex Repository::storeMutex;

int Repository::numStores = 0;
unsigned long int Repository::contador = 0;


string Repository::hash_key(string _url){
	return md5.getHashFromString(_url);
}

Repository::Repository(){
//	fileToDisk[0]    = fopen("./disco/a_0", "a");
//	fileToDisk[0+16] = fopen("./disco/i_0", "a");
//	fileToDisk[1]    = fopen("./disco/a_1", "a");
//	fileToDisk[1+16] = fopen("./disco/i_1", "a");
//	fileToDisk[2]    = fopen("./disco/a_2", "a");
//	fileToDisk[2+16] = fopen("./disco/i_2", "a");
//	fileToDisk[3]    = fopen("./disco/a_3", "a");
//	fileToDisk[3+16] = fopen("./disco/i_3", "a");
//	fileToDisk[4]    = fopen("./disco/a_4", "a");
//	fileToDisk[4+16] = fopen("./disco/i_4", "a");
//	fileToDisk[5]    = fopen("./disco/a_5", "a");
//	fileToDisk[5+16] = fopen("./disco/i_5", "a");
//	fileToDisk[6]    = fopen("./disco/a_6", "a");
//	fileToDisk[6+16] = fopen("./disco/i_6", "a");
//	fileToDisk[7]    = fopen("./disco/a_7", "a");
//	fileToDisk[7+16] = fopen("./disco/i_7", "a");
//	fileToDisk[8]    = fopen("./disco/a_8", "a");
//	fileToDisk[8+16] = fopen("./disco/i_8", "a");
//	fileToDisk[9]    = fopen("./disco/a_9", "a");
//	fileToDisk[9+16] = fopen("./disco/i_9", "a");
//	fileToDisk[10]    = fopen("./disco/a_a", "a");
//	fileToDisk[10+16] = fopen("./disco/i_a", "a");
//	fileToDisk[11]    = fopen("./disco/a_b", "a");
//	fileToDisk[11+16] = fopen("./disco/i_b", "a");
//	fileToDisk[12]    = fopen("./disco/a_c", "a");
//	fileToDisk[12+16] = fopen("./disco/i_c", "a");
//	fileToDisk[13]    = fopen("./disco/a_d", "a");
//	fileToDisk[13+16] = fopen("./disco/i_d", "a");
//	fileToDisk[14]    = fopen("./disco/a_e", "a");
//	fileToDisk[14+16] = fopen("./disco/i_e", "a");
//	fileToDisk[15]    = fopen("./disco/a_f", "a");
//	fileToDisk[15+16] = fopen("./disco/i_f", "a");
//	cout << "AQUI, construtor " << endl;
	
	 contadorPaginas.setFile(Config::getPageCountFileName());

}

Repository::~Repository(){
	//fecha arquivos do repositorio
//	for(int i=0; i<32; i++){
//		fclose(fileToDisk[i]);
//		fileToDisk[i] == NULL;
//	}
//	cout << "Aqui destruiu... " << endl;
}


void Repository::oldStore(string _url, string _page){
	
	ofstream fileToDisk;
	
	string hash;
	hash = "./disco/" + hash_key(_url);
		
	fileToDisk.open(hash.c_str());
	
	if ( (fileToDisk.rdstate() & ifstream::eofbit ) != 0 )
	    	cout << "Error opening eof\n";
	if ( (fileToDisk.rdstate() & ifstream::failbit ) != 0 )
		    cout << "Error opening fail\n";
	if ( (fileToDisk.rdstate() & ifstream::badbit ) != 0 )
		    cout << "Error opening bad\n";
	if ( (fileToDisk.rdstate() & ifstream::goodbit ) != 0 )
	    	cout << "Error opening good\n";
	if(fileToDisk.fail()){
		cout << "numFalhasOpen: " << numStores << endl;
		cout << "hash: " << hash.c_str() << endl;
		//cout << "_page.c_str(): " << _page.c_str() << endl;
		cout << "_page.size() " << _page.size() << endl;
		fileToDisk.clear();
	}
	
	fileToDisk.write(_page.c_str(), _page.size());	
	
	if(fileToDisk.fail()){
		cout << "numFalhasWrite: " << numStores << endl;
		cout << "hash: " << hash.c_str() << endl;
		//cout << "_page.c_str(): " << _page.c_str() << endl;
		cout << "_page.size() " << _page.size() << endl;
		cout << endl;
		fileToDisk.clear();
	}
	
	numStores++;
	if(!(numStores%100)) cout << "numStores: " << numStores << endl; 
	
	fileToDisk.close();	
}



void Repository::newOldStore(string _url, string _page){
	ofstream fileToDisk;
	if(_url == "") return;  // elimina string vazia
	try{
	
		string hash = hash_key(_url);
		string folder;
		
		folder = "./disco/" + hash.substr(0,2);
		folder = folder + "/" + hash;
		
		//cout << "Folder:  Repository::newOldStore    " << folder << endl;
		
		fileToDisk.open(folder.c_str());
		
		if ( (fileToDisk.rdstate() & ifstream::eofbit ) != 0 )	cout << "Error opening eof\n";
		if ( (fileToDisk.rdstate() & ifstream::failbit ) != 0 )	 cout << "Error opening fail\n";
		if ( (fileToDisk.rdstate() & ifstream::badbit ) != 0 )   cout << "Error opening bad\n";
		if ( (fileToDisk.rdstate() & ifstream::goodbit ) != 0 ) 	cout << "Error opening good\n";
		
		if(fileToDisk.fail()){
			cout << "numFalhasOpen: " << numStores << endl;
			cout << "hash: " << hash.c_str() << endl;
			//cout << "_page.c_str(): " << _page.c_str() << endl;
			cout << "_page.size() " << _page.size() << endl;
			fileToDisk.clear();
			return;
		}
		
		fileToDisk.write(_page.c_str(), _page.size());	
		
		if(fileToDisk.fail()){
			cout << "numFalhasWrite: " << numStores << endl;
			cout << "hash: " << hash.c_str() << endl;
			//cout << "_page.c_str(): " << _page.c_str() << endl;
			cout << "_page.size() " << _page.size() << endl;
			cout << endl;
			fileToDisk.clear();
			return;
		}
		
		numStores++;
		if(!(numStores%100)) cout << "numStores: " << numStores << endl; 
		
		fileToDisk.close();	
	
	}catch(...){
		cout << "Algo no Repository::newOldStore deu errado... " << endl;
	}
}



void Repository::store(string _url, string _page){

//	oldStore(_url, _page);
	newOldStore(_url, _page);
	
//    dados estatisticos de paginas copiadas
    time_t timeMc;
    time(&timeMc);
    contador++;
    if(( contador % 100) == 1) contadorPaginas.addData((unsigned long int) timeMc, contador);

	return;

	
	
	
    boost::mutex::scoped_lock storeLock(storeMutex);
	
	int chave = 0;
	string hash;
	hash = hash_key(_url);	
	mapa[hash] = "page";   //TEMPORARIO

	try {
		char prim = hash[0];
		if (prim >= '0' && prim<='9') chave = int(prim-'0');
		else 			              chave = int(prim-'a') + 10;
		
		if(fileToDisk[chave] == NULL || fileToDisk[chave + 16] == NULL){
			numStores++;
			cout << "numFalhasNULL: " << numStores << endl;
			cout << "hash: " << hash.c_str() << endl;
			//cout << "_page.c_str(): " << _page.c_str() << endl;
			cout << "_page.size() " << _page.size() << endl;
			cout << endl;	
			
		}else {
			long int pos;
			pos =  ftell (fileToDisk[chave]);
			fwrite(_page.c_str(),_page.size(),1,fileToDisk[chave]);
			fprintf(fileToDisk[chave + 16],"%ld\n", pos);
			fflush(fileToDisk[chave + 16]);
//			cout << "Escreveu no i_" << dec << chave << " = " << pos << endl;
		}		
	}
	catch (...) {
		cout << "MC sempre cagando o pau na Repository::store!!! " << endl;
	}	
}

bool Repository::isEmpty(string hash){
	  it = mapa.find(hash);
	  if( it == mapa.end() ) return true;
	  else                   return false;
}

int Repository::size(){
	return (unsigned int) mapa.size();
}

string Repository::getPage(string _url){
	string hash = hash_key(_url);
	if(!isEmpty(hash)) return mapa[hash];
	else return "";
}
